package cn.org.xwb.bookmall.user.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.org.xwb.bookmall.model.constant.AccountConst;
import cn.org.xwb.bookmall.model.mapper.UserInfoMapper;
import cn.org.xwb.bookmall.model.mapper.UserMapper;
import cn.org.xwb.bookmall.model.po.User;
import cn.org.xwb.bookmall.model.po.UserAllInfo;
import cn.org.xwb.bookmall.model.po.UserInfo;
import cn.org.xwb.bookmall.model.properties.AccountProp;
import cn.org.xwb.bookmall.model.properties.MailProp;
import cn.org.xwb.bookmall.model.utils.BookCodeUtil;
import cn.org.xwb.bookmall.model.utils.BookRedisUtil;
import cn.org.xwb.bookmall.model.vo.UserAllInfoVo;
import cn.org.xwb.bookmall.model.vo.UserVo;
import cn.org.xwb.bookmall.user.service.XwbUserService;
import cn.org.xwb.common.encrypt.utils.SHAUtil;
import cn.org.xwb.common.model.vo.RespResult;
import cn.org.xwb.common.redis.utils.RedisUtil;
import cn.org.xwb.common.utils.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.base.Verify;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
public class XwbUserServiceImpl implements XwbUserService {

    @Autowired
    private BookRedisUtil bookRedisUtil;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private AccountProp accountProp;

    @Autowired
    private BookCodeUtil bookCodeUtil;

    @Autowired
    private MailProp mailProp;

    @Autowired
    private UserInfoMapper userInfoMapper;

    private String getKeyAccount(String account) {
        return bookRedisUtil.getRedisKeyUser(AccountConst.ACCOUNT, account);
    }

    private UserAllInfo findAndCache(Long id) {
        UserAllInfo userAllInfo = userMapper.selectUserAllInfoById(id);
        // 缓存
        boolean res = redisUtil.setVal(
                getKeyAccount(userAllInfo.getAccount()),
                userAllInfo,
                accountProp.getAccountCacheExpireUser(),
                accountProp.getAccountCacheExpireUserUnit()
        );
        if (!res) return null;
        return userAllInfo;
    }

    private UserAllInfo findFromCache(String account) {
        return ((UserAllInfo) redisUtil.get(getKeyAccount(account)));
    }

    @Override
    public RespResult<Object> getUserAllInfo(String account) {
        // 从缓存中获取
        UserAllInfo userAllInfo = findFromCache(account);
        UserAllInfoVo userAllInfoVo = Convert.convert(UserAllInfoVo.class, userAllInfo);
        return RespResult.success("获取用户信息成功", userAllInfoVo);
    }

    @Override
    public RespResult<Object> updateAvatar(String avatar, Long id) {
        User user = new User();
        user.setId(id);
        user.setAvatar(avatar);
        int res = userMapper.updateById(user);
        if (res < 1) return RespResult.fail("更换头像失败，请重新尝试");
        // 刷新缓存
        UserAllInfo userAllInfo = findAndCache(id);
        if (Objects.isNull(userAllInfo)) return RespResult.fail("更换头像失败，请重新尝试");
        return RespResult.success("更换头像成功", Convert.convert(UserVo.class, userAllInfo));
    }

    @Override
    public RespResult<Object> updateAvatarBg(String avatarBg, Long id) {
        User user = new User();
        user.setId(id);
        user.setAvatarBg(avatarBg);
        int res = userMapper.updateById(user);
        if (res < 1) return RespResult.fail("更换头像背景失败，请重新尝试");
        // 刷新缓存
        UserAllInfo userAllInfo = findAndCache(id);
        if (Objects.isNull(userAllInfo)) return RespResult.fail("更换头像背景失败，请重新尝试");
        return RespResult.success("更换头像背景成功", Convert.convert(UserVo.class, userAllInfo));
    }

    @Override
    public RespResult<Object> updateUsername(String username, Long id) {
        // 查询用户名是否存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        User user = userMapper.selectOne(queryWrapper);
        // 用户名已存在
        if (Objects.nonNull(user) && user.getId() != id) return RespResult.fail("用户名已存在");
        // 用户名不存在，更新用户名
        user = new User();
        user.setId(id);
        user.setUsername(username);
        int res = userMapper.updateById(user);
        // 更换用户名失败
        if (res < 1) return RespResult.fail("更换用户名失败，请重新尝试");
        // 刷新缓存
        UserAllInfo userAllInfo = findAndCache(id);
        // 用户缓存信息刷新失败
        if (Objects.isNull(userAllInfo)) return RespResult.fail("更换用户名失败，请重新尝试");
        // 更换用户名成功
        return RespResult.success("更换用户名成功", Convert.convert(UserVo.class, userAllInfo));
    }

    @Override
    public RespResult<Object> getUpdatePwdMailCode(String mail, String account) {
        // 判断是否可以再次获取
        String mailCodeAgain = getMailCode("update:pwd:again:%s", account);
        // 不能再次获取
        if (Objects.nonNull(mailCodeAgain)) return RespResult.fail("请勿频繁获取验证码");
        // 发送验证码
        String code = RandomUtil.genRandNumStr(mailProp.getCodeLen());
        if (!bookCodeUtil.sendCode(mail, code)) return RespResult.fail("发送验证码失败");
        // 缓存
        if (!cacheMailCode("update:pwd:%s", account, code)) return RespResult.fail("发送验证码失败");
        if (!cacheMailCodeAgain("update:pwd:again:%s", account, code)) return RespResult.fail("发送验证码失败");
        return RespResult.success("发送验证码成功");
    }

    @Override
    public RespResult<Object> updatePwd(String code, String account, String pwd, Long id) {
        String code_cache = getMailCode("update:pwd:%s", account);
        // 查询验证码是否有效
        RespResult<Object> respResult = BookCodeUtil.judgeCode(code, code_cache);
        // 验证码失效或错误
        if (Objects.nonNull(respResult)) return respResult;
        // 从缓存获取用户信息
        UserAllInfo userAllInfo = (UserAllInfo) redisUtil.get(bookRedisUtil.getRedisKeyUser(AccountConst.ACCOUNT, account));
        // 修改密码
        User user = Convert.convert(User.class, userAllInfo);
        // 对密码进行加密处理
        user.setPassword(SHAUtil.encryptBySHA256(user.getSalt(), pwd));
        // 更新数据库
        int res = userMapper.updateById(user);
        // 更新数据库失败，修改密码失败
        if (res < 1) return RespResult.fail("修改密码失败，请重新尝试");
        // 刷新缓存的用户信息
        userAllInfo = findAndCache(id);
        if (Objects.isNull(userAllInfo)) return RespResult.fail("修改密码失败，请重新尝试");
        return RespResult.success("修改密码成功");
    }

    private String getMailCode(String pre, String sub) {
        return redisUtil.getStr(bookRedisUtil.getRedisKeyUser(pre, sub));
    }

    private Boolean cacheMailCode(String pre, String sub, String code) {
        return redisUtil.setVal(
                bookRedisUtil.getRedisKeyUser(pre, sub),
                code,
                mailProp.getCodeExpire(),
                mailProp.getCodeExpireUnit()
        );
    }

    private Boolean cacheMailCodeAgain(String pre, String sub, String code) {
        return redisUtil.setVal(
                bookRedisUtil.getRedisKeyUser(pre, sub),
                code,
                mailProp.getCodeAgain(),
                mailProp.getCodeExpireUnit()
        );
    }

    @Override
    public RespResult<Object> updateUserInfo(String phone, String gender, Long id) {
        if (!Validator.isMobile(phone)) return RespResult.fail("手机号不合法，请重新尝试");
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfo::getUserId, id);
        UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);
        int res = 0;
        if (Objects.isNull(userInfo)) {
            userInfo = new UserInfo();
            userInfo.setUserId(id);
            userInfo.setGender(gender.equals("1"));
            userInfo.setPhone(phone);
            res = userInfoMapper.insert(userInfo);
        } else {
            userInfo.setGender(gender.equals("1"));
            userInfo.setPhone(phone);
            res = userInfoMapper.updateById(userInfo);
        }
        if (res < 1) return RespResult.fail("修改信息失败，请重新尝试");
        // 刷新缓存
        UserAllInfo userAllInfo = findAndCache(id);
        if (Objects.isNull(userAllInfo)) return RespResult.fail("修改信息失败，请重新尝试");
        return RespResult.success("修改信息成功");
    }
}
